package com.healthyeating.manage.service.impl;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.healthyeating.common.core.domain.entity.SysDictData;
import com.healthyeating.common.utils.DateUtils;
import com.healthyeating.common.utils.SecurityUtils;
import com.healthyeating.manage.domain.*;
import com.healthyeating.manage.domain.dto.ChartDto;
import com.healthyeating.manage.mapper.AssocNutritionMapper;
import com.healthyeating.manage.mapper.DrinkMapper;
import com.healthyeating.manage.mapper.EatMapper;
import com.healthyeating.manage.mapper.NutritionMapper;
import com.healthyeating.manage.service.IChartService;
import com.healthyeating.system.service.ISysDictTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 图表展示Service业务层处理
 *
 * @author 段奇乐
 * @date 2024-01-07
 */
@Service
public class ChartServiceImpl implements IChartService {

    @Autowired
    private EatMapper eatMapper;

    @Autowired
    private DrinkMapper dinkMapper;

    @Autowired
    private AssocNutritionMapper assocNutritionMapper;

    @Autowired
    private NutritionMapper nutritionMapper;

    @Autowired
    private ISysDictTypeService sysDictTypeService;

    /**
     * 将waterType从数字转换为对应的dictLabel
     */
    private String getWaterDictLabel(Integer waterType) {
        List<SysDictData> waterTypeList = sysDictTypeService.selectDictDataByType("water_type");
        if (waterTypeList != null) {
           for (SysDictData water : waterTypeList) {
               if (water.getDictValue().equals(waterType.toString())) {
                   return water.getDictLabel();
               }
           }
        }
        return null;
    }

    /**
     * 获取每日进食数据
     *
     * @return 去重后的每日进食数据
     */
    private List<Eat> getSortEatList() {
        Eat eat = new Eat();
        Map<String, Object> map = DateUtils.extractTime(DateUtils.getNowDate());
        eat.setStartOfDay((Date) map.get("startOfDay"));
        eat.setEndOfDay((Date) map.get("endOfDay"));
        eat.setUserId(SecurityUtils.getUserId());
        List<Eat> eatList = eatMapper.selectEatList(eat);

        // 按eatName去重，并合并amount
        return new ArrayList<>(eatList.stream()
                .collect(Collectors.toMap(
                        Eat::getEatName,
                        Function.identity(),
                        (existing, replacement) -> {
                            existing.setAmount(existing.getAmount() + replacement.getAmount());
                            return existing;
                        },
                        LinkedHashMap::new
                ))
                .values());
    }

    /**
     * 获取每日饮水数据
     *
     * @return 去重后的每日饮水数据
     */
    private List<Drink> getSortDrinkList() {
        Drink drink = new Drink();
        Map<String, Object> map = DateUtils.extractTime(DateUtils.getNowDate());
        drink.setStartOfDay((Date) map.get("startOfDay"));
        drink.setEndOfDay((Date) map.get("endOfDay"));
        drink.setUserId(SecurityUtils.getUserId());
        return dinkMapper.selectDrinkList(drink);
    }


    /**
     * 每日进食营养成分数据处理
     *
     * @return 处理后的数据
     */
    @Override
    public List<Chart> dealWithEatChart() {
        List<Eat> sortEatList = getSortEatList();
        List<Chart> chartList = new ArrayList<>();
        Map<String, Object> map = new HashMap<>();
        map.put("source", "tb_eat");
        sortEatList.forEach(sortEat -> {
            map.put("dietName", sortEat.getEatName());
            AssocNutrition assocNutrition = assocNutritionMapper.selectAssocNutritionByDietName(map);
            if (assocNutrition != null) {
                JSONArray assocNutritionArr = JSONUtil.parseArray(assocNutrition.getIngredients());
                List<DietNutrition> dietNutritionList = JSONUtil.toList(assocNutritionArr, DietNutrition.class);
                dietNutritionList.forEach(dietNutrition -> {
                    Nutrition nutrition = nutritionMapper.selectNutritionByNutritionName(dietNutrition.getNutritionName());
                    if (nutrition != null && nutrition.getNutritionNrv() != 0) {
                        BigDecimal nrv = BigDecimal.valueOf(sortEat.getAmount() * (dietNutrition.getNutritionNrv() / 100))
                                .setScale(2, RoundingMode.HALF_UP);
                        Chart chart = new Chart();
                        chart.setName(nutrition.getNutritionName());
                        chart.setValues(Collections.singletonList(nrv.doubleValue()));
                        chartList.add(chart);
                    }
                });
            }
        });

        // 按照name分组，并合并values
        Map<String, List<Chart>> groupedByName = chartList.stream()
                .collect(Collectors.groupingBy(Chart::getName));
        List<Chart> mergedChartList = new ArrayList<>();
        for (Map.Entry<String, List<Chart>> entry : groupedByName.entrySet()) {
            Chart mergedChart = new Chart();
            mergedChart.setName(entry.getKey());
            // 合并values
            Double mergedValues = entry.getValue().stream()
                    .flatMap(chart -> chart.getValues().stream())
                    .mapToDouble(Double::doubleValue)
                    .sum();
            // 查找单位
            Nutrition nutrition = nutritionMapper.selectNutritionByNutritionName(entry.getKey());
            mergedChart.setUnit(nutrition.getNutritionUnit());
            mergedChart.setValues(Collections.singletonList(mergedValues));
            mergedChartList.add(mergedChart);
        }
        return mergedChartList;
    }

    /**
     * 每日饮水营养成分数据处理
     *
     * @return 处理后的数据
     */
    @Override
    public List<Chart> dealWithDrinkChart() {
        List<Chart> chartList = new ArrayList<>();
        List<Drink> sortDrinkList = getSortDrinkList();
        Map<String, Object> map = new HashMap<>();
        map.put("source", "tb_drink");
        sortDrinkList.forEach(sortDrink -> {
            // 将waterType转换为对应的dictLabel
            String waterType = getWaterDictLabel(sortDrink.getWaterType());
            map.put("dietName", waterType);
            AssocNutrition assocNutrition = assocNutritionMapper.selectAssocNutritionByDietName(map);
            if (assocNutrition != null) {
                JSONArray assocNutritionArr = JSONUtil.parseArray(assocNutrition.getIngredients());
                List<DietNutrition> dietNutritionList = JSONUtil.toList(assocNutritionArr, DietNutrition.class);
                dietNutritionList.forEach(dietNutrition -> {
                    Nutrition nutrition = nutritionMapper.selectNutritionByNutritionName(dietNutrition.getNutritionName());
                    if (nutrition != null && nutrition.getNutritionNrv() != 0) {
                        BigDecimal nrv = BigDecimal.valueOf(sortDrink.getTodayDrink() * (dietNutrition.getNutritionNrv() / 100))
                                .setScale(2, RoundingMode.HALF_UP);
                        Chart chart = new Chart();
                        chart.setName(nutrition.getNutritionName());
                        chart.setRefValue(nrv.doubleValue());
                        chart.setUnit(nutrition.getNutritionUnit());
                        chartList.add(chart);
                    }
                });
            }
        });
        return chartList;
    }

    /**
     * 每日进食营养成分数据处理（占比）
     *
     * @return 处理后的数据
     */
    @Override
    public List<ChartDto> dealWithEatPercentChart() {
        List<Eat> sortEatList = getSortEatList();
        List<ChartDto> chartDtoList = new ArrayList<>();
        Map<String, Object> map = new HashMap<>();
        map.put("source", "tb_eat");
        sortEatList.forEach(sortEat -> {
            map.put("dietName", sortEat.getEatName());
            AssocNutrition assocNutrition = assocNutritionMapper.selectAssocNutritionByDietName(map);
            if (assocNutrition != null) {
                JSONArray assocNutritionArr = JSONUtil.parseArray(assocNutrition.getIngredients());
                List<DietNutrition> dietNutritionList = JSONUtil.toList(assocNutritionArr, DietNutrition.class);
                List<Chart> chartList = new ArrayList<>();
                dietNutritionList.forEach(dietNutrition -> {
                    Nutrition nutrition = nutritionMapper.selectNutritionByNutritionName(dietNutrition.getNutritionName());
                    if (nutrition !=null && nutrition.getNutritionNrv() != 0) {
                        BigDecimal nrv = BigDecimal.valueOf((dietNutrition.getNutritionNrv() / nutrition.getNutritionNrv()) * 100)
                                .setScale(2, RoundingMode.HALF_UP);
                        Chart chart = new Chart();
                        chart.setName(nutrition.getNutritionName());
                        chart.setRefValue(nrv.doubleValue());
                        chart.setUnit(nutrition.getNutritionUnit());
                        chartList.add(chart);
                    }
                });
                ChartDto chartDto = new ChartDto();
                chartDto.setDietName(sortEat.getEatName());
                chartDto.setNutrition(chartList);
                chartDtoList.add(chartDto);
            }
        });
        return chartDtoList;
    }

    /**
     * 近7日的进食营养成分数据处理
     *
     * @return 处理后的数据
     */
    @Override
    public List<ChartDto> dealWithEatChartWithDay() {
        // 查询以当天为结束时间，往前7天为开始时间的数据，包含当天，共七天
        Eat eat = new Eat();
        Map<String, Object> map = DateUtils.extractTime(DateUtils.getNowDate());
        // 计算开始时间（当前日期往前推6天）
        Calendar calendar = Calendar.getInstance();
        calendar.setTime((Date) map.get("startOfDay"));
        calendar.add(Calendar.DAY_OF_YEAR, -6); // 减去6天，得到7天前的日期
        Date startOfDay = calendar.getTime();
        // 设置查询条件
        eat.setStartOfDay(startOfDay);
        eat.setEndOfDay((Date) map.get("endOfDay"));
        eat.setUserId(SecurityUtils.getUserId());
        // 查询7天内的进食数据
        List<Eat> eatList = eatMapper.selectEatList(eat);
        // 按日期和eatName分组，合并同一天的相同eatName的amount
        Map<String, Map<String, Double>> dailyEatMap = eatList.stream()
                .collect(Collectors.groupingBy(
                        item -> {
                            // 格式化日期，只保留年月日
                            Date date = item.getCreateTime();
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                            return sdf.format(date);
                        }, // 按日期分组
                        Collectors.groupingBy(
                                Eat::getEatName, // 按eatName分组
                                Collectors.summingDouble(Eat::getAmount) // 合并amount
                        )
                ));
        // 按日期分组，合并同一天的营养成分
        Map<String, Map<String, Double>> dailyNutritionMap = new HashMap<>();
        Map<String, Object> assocMap = new HashMap<>();
        assocMap.put("source", "tb_eat");

        for (Map.Entry<String, Map<String, Double>> dailyEntry : dailyEatMap.entrySet()) {
            String dietDate = dailyEntry.getKey(); // 格式化后的日期（yyyy-MM-dd）
            Map<String, Double> eatNameAmountMap = dailyEntry.getValue();
            // 初始化当天的营养成分Map
            dailyNutritionMap.putIfAbsent(dietDate, new HashMap<>());
            for (Map.Entry<String, Double> eatEntry : eatNameAmountMap.entrySet()) {
                String eatName = eatEntry.getKey();
                double amount = eatEntry.getValue();
                assocMap.put("dietName", eatName);

                // 查询食物的营养成分
                AssocNutrition assocNutrition = assocNutritionMapper.selectAssocNutritionByDietName(assocMap);
                if (assocNutrition != null) {
                    JSONArray assocNutritionArr = JSONUtil.parseArray(assocNutrition.getIngredients());
                    List<DietNutrition> dietNutritionList = JSONUtil.toList(assocNutritionArr, DietNutrition.class);

                    // 累加当天的营养成分
                    for (DietNutrition dietNutrition : dietNutritionList) {
                        String nutritionName = dietNutrition.getNutritionName();
                        double nutritionValue = amount * (dietNutrition.getNutritionNrv() / 100);

                        // 保留两位小数
                        BigDecimal bd = new BigDecimal(nutritionValue).setScale(2, RoundingMode.HALF_UP);
                        nutritionValue = bd.doubleValue();

                        dailyNutritionMap.get(dietDate).merge(nutritionName, nutritionValue, Double::sum);
                    }
                }
            }
        }

        // 构造返回结果
        List<ChartDto> result = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (Map.Entry<String, Map<String, Double>> entry : dailyNutritionMap.entrySet()) {
            String dietDate = entry.getKey(); // 格式化后的日期（yyyy-MM-dd）
            Map<String, Double> nutritionMap = entry.getValue();
            // 构造营养成分列表
            List<Chart> nutritionList = new ArrayList<>();
            for (Map.Entry<String, Double> nutritionEntry : nutritionMap.entrySet()) {
                String nutritionName = nutritionEntry.getKey();
                double nutritionValue = nutritionEntry.getValue();
                // 查询营养成分的单位
                Nutrition nutrition = nutritionMapper.selectNutritionByNutritionName(nutritionName);
                String unit = (nutrition != null) ? nutrition.getNutritionUnit() : null;
                // 构造Chart对象
                Chart chart = new Chart();
                chart.setName(nutritionName);
                chart.setRefValue(nutritionValue); // 已经保留两位小数
                chart.setUnit(unit); // 设置单位
                nutritionList.add(chart);
            }
            // 构造ChartDto对象
            ChartDto chartDto = new ChartDto();
            try {
                // 将字符串日期转换为Date
                chartDto.setDietTime(sdf.parse(dietDate));
            } catch (ParseException e) {
                // 如果日期解析失败，记录日志并跳过该数据
                continue;
            }
            chartDto.setNutrition(nutritionList);
            result.add(chartDto);
        }
        return result;
    }
}